package service

import (
	"errors"
	"fmt"
	"github.com/casbin/casbin"
	"github.com/casbin/casbin/util"
	gormadapter "github.com/casbin/gorm-adapter"
	"perfect-admin/dao"
	"perfect-admin/global"
	"perfect-admin/model"
	"perfect-admin/model/request"
	"perfect-admin/model/response"
	"strings"
	"sync"
)

func UpdateCasbin2(casbinRecive request.CasbinInReceive) error {
	e := Casbin()
	// 清空指定用户所有权限，若更新权限为空 则返回
	ClearCasbin(e, 0, casbinRecive.RoleId)
	if len(casbinRecive.CasbinInfos) == 0 {
		return nil
	}
	// 更新用户权限
	var ChanController response.ChannelController
	ChanController.Channel = make(chan bool, len(casbinRecive.CasbinInfos))
	ChanController.WaitGroup = &sync.WaitGroup{}
	ChanController.WaitGroup.Add(len(casbinRecive.CasbinInfos))
	for _, v := range casbinRecive.CasbinInfos {
		go AddCasbin(e, v, casbinRecive.RoleId, ChanController)
	}
	ChanController.WaitGroup.Wait()
	close(ChanController.Channel)

	for addFlag := range ChanController.Channel{
		if !addFlag {
			return errors.New("存在相同api,添加失败,请联系管理员")
		}
	}
	return nil
}

func UpdateCasbin(casbinRecive request.CasbinInReceive) (err error) {
	dao.DB.Where("v0 = ?", casbinRecive.RoleId).Delete(model.CasbinRule{})
	if len(casbinRecive.CasbinInfos) == 0 {
		return nil
	}
	sql := "insert into casbin_rule(p_type, v0, v1, v2) values "
	for i, casbinInfo := range casbinRecive.CasbinInfos {
		if i == len(casbinRecive.CasbinInfos)-1{
			sql += fmt.Sprintf("('p','%s','%s','%s');", casbinRecive.RoleId, casbinInfo.Path, casbinInfo.Method)
		} else {
			sql += fmt.Sprintf("('p','%s','%s','%s'),", casbinRecive.RoleId, casbinInfo.Path, casbinInfo.Method)
		}
	}
	if err = dao.DB.Exec(sql).Error; err!= nil{
		return err
	}
	return nil
}

func AddCasbin(e *casbin.Enforcer, v request.CasbinInfo, roleId string, chan_contro response.ChannelController) {
	defer chan_contro.WaitGroup.Done()
	cm := model.CasbinRule{
		Ptype:       "p",
		RoleId: roleId,
		Path:        v.Path,
		Method:      v.Method,
	}
	chan_contro.Channel <- e.AddPolicy(cm.RoleId, cm.Path, cm.Method)
}

func UpdateCasbinApi(oldPath string, newPath string, oldMethod string, newMethod string) error {
	var cs []model.CasbinRule
	err := dao.DB.Table("casbin_rule").Where("v1 = ? AND v2 = ?", oldPath, oldMethod).Find(&cs).Updates(map[string]string{
		"v1": newPath,
		"v2": newMethod,
	}).Error
	return err
}

func GetPolicyPathByRoleId(roleId string) (pathMaps []request.CasbinInfo) {
	e := Casbin()
	list := e.GetFilteredPolicy(0, roleId)
	for _, v := range list {
		pathMaps = append(pathMaps, request.CasbinInfo{
			Path:   v[1],
			Method: v[2],
		})
	}
	return pathMaps
}

func GetPolicyNodeByRoleId(roleId string) (ApidefKeys []string) {
	e := Casbin()
	list := e.GetFilteredPolicy(0, roleId)
	for _, v := range list {
		ApidefKeys = append(ApidefKeys, "p:"+v[1]+"m:"+v[2])
	}
	return ApidefKeys
}

func ClearCasbin(e *casbin.Enforcer, v int, p ...string) bool {
	return e.RemoveFilteredPolicy(v, p...)

}

func Casbin() *casbin.Enforcer {
	a := gormadapter.NewAdapterByDB(dao.DB)
	e := casbin.NewEnforcer(global.CONFIG.Casbin.ModelPath, a)
	e.AddFunction("ParamsMatch", ParamsMatchFunc)
	_ = e.LoadPolicy()
	return e
}


func ParamsMatch(fullNameKey1 string, key2 string) bool {
	key1 := strings.Split(fullNameKey1, "?")[0]
	//剥离路径后再使用casbin的keyMatch2
	return util.KeyMatch2(key1, key2)
}


func ParamsMatchFunc(args ...interface{}) (interface{}, error) {
	name1 := args[0].(string)
	name2 := args[1].(string)

	return (bool)(ParamsMatch(name1, name2)), nil
}
